Bootloader hardening#

Boot parameters#

Boot parameters pass settings to the kernel at boot using your bootloader. Some settings can be used to increase security, similar to kernel parameters.

Below offered a way to change the boot options for the GRUB bootloader, which is used in Ubuntu and Arch Linux. Now you should see something like this: Open file /etc/default/grub:

sudo -e /etc/default/grub

Find the line GRUB_CMDLINE_LINUX_DEFAULT=.... It may looks like:

GRUB_CMDLINE_LINUX_DEFAULT="quiet splash loglevel=3"

The value of this parameter is the boot parameters currently in use. To add a new boot parameter there, simply insert it separated by a space from the previous one.

The following settings are recommended to increase security.

slab_nomerge

This disables slab merging, which significantly increases the difficulty of heap exploitation and limits cross-subsystem memory corruption.

init_on_alloc=1 init_on_free=1

This enables zeroing of memory during allocation and free time, which can help mitigate use-after-free vulnerabilities and erase sensitive information in memory.

page_alloc.shuffle=1

This option randomises page allocator freelists, improving security by making page allocations less predictable.

randomize_kstack_offset=on

This option randomises the kernel stack offset on each syscall, which makes attacks that rely on deterministic kernel stack layout significantly more difficult.

vsyscall=none

This disables vsyscalls, as they are obsolete and have been replaced with vDSO. Vsyscalls are also at fixed addresses in memory, making them a potential target for ROP attacks.

debugfs=off

This disables debugfs, which exposes a lot of sensitive information about the kernel.

oops=panic

Sometimes certain kernel exploits will cause what is known as an "oops". This parameter will cause the kernel to panic on such oopses, thereby preventing those exploits. However, sometimes bad drivers cause harmless oopses which would result in your system crashing, meaning this boot parameter can only be used on certain hardware.

module.sig_enforce=1

This only allows kernel modules that have been signed with a valid key to be loaded, which increases security by making it much harder to load a malicious kernel module. This prevents all out-of-tree kernel modules, including DKMS modules from being loaded unless you have signed them, meaning that modules such as the VirtualBox or Nvidia drivers may not be usable, although that may not be important, depending on your setup.

lockdown=confidentiality

This enables Kernel Lockdown feature, which is designed to prevent both direct and indirect access to a running kernel image, attempting to protect against unauthorized modification of the kernel image and to prevent access to security and cryptographic data located in kernel memory, whilst still permitting driver modules to be loaded.

mce=0

This causes the kernel to panic on uncorrectable errors in ECC memory which could be exploited. This is unnecessary for systems without ECC memory.

quiet loglevel=0

These parameters prevent information leaks during boot in combination with the kernel.printk kernel parameter, documented earlier.

random.trust_cpu=off

This disables trusting the use of the CPU's random number generator to fully seed the kernel's CRNG, since it often have serious vulnerabilities, and there is no way to check its implementation.

ipv6.disable=1

This disables the entire IPv6 stack which may not be required if you have not migrated to it. Do not use this boot parameter if you are using IPv6.

amd_iommu=on
intel_iommu=on

Direct memory access (DMA) attacks involve gaining complete access to all of system memory by inserting certain physical devices. This can be mitigated via an IOMMU, which controls the areas of memory accessible to devices.

efi=disable_early_pci_dma

This option fixes a hole in the above IOMMU by disabling the busmaster bit on all PCI bridges during very early boot.

Now you should see something like this:

GRUB_CMDLINE_LINUX_DEFAULT="quiet splash loglevel=0 slab_nomerge init_on_alloc=1 init_on_free=1 page_alloc.shuffle=1 vsyscall=none debugfs=off oops=panic module.sig_enforce=1 lockdown=confidentiality mce=0 random.trust_cpu=off amd_iommu=on efi=disable_early_pci_dma"

After saving the file, you need to update the GRUB configuration:

sudo grub-mkconfig -o /boot/grub/grub.cfg

You need to reboot for the changes to take effect.

GRUB password#

To set a password for GRUB, you need to run the command:

grub-mkpasswd-pbkdf2

Come up with a decent password. After entering the password, a string should be generated. For example, grub.pbkdf2.sha512.10000.C4009....

Open the file /etc/grub.d/40_password:

sudo -e /etc/grub.d/40_password

And add to it:

set superusers="grubadm"
password_pbkdf2 grubadm <password>

Where the <password> should be replaced with the previously generated string. For example:

set superusers="grubadm"
password_pbkdf2 grubadm grub.pbkdf2.sha512.10000.C4009...

Then open the file /etc/default/grub:

sudo -e /etc/default/grub

Find the line:

#GRUB_DISABLE_SUBMENU=y

And uncomment it:

GRUB_DISABLE_SUBMENU=y

The password has been set, but now you have to enter the password every time you boot, which can be very inconvenient. To solve this problem, let's allow booting into the system without a password.

Open file /etc/grub.d/10_linux:

sudo -e /etc/grub.d/10_linux

And find the following strings:

echo "menuentry '$(echo "$title" | grub_quote)' ${CLASS} \$menuentry_id_option...

echo "menuentry '$(echo "$os" | grub_quote)' ${CLASS} \$menuentry_id_option...

You can search for the string ${CLASS} in your editor.

In these two lines, after ${CLASS} add the line --unrestricted:

echo "menuentry '$(echo "$title" | grub_quote)' ${CLASS} --unrestricted \$menuentry_id_option...

echo "menuentry '$(echo "$os" | grub_quote)' ${CLASS} --unrestricted \$menuentry_id_option...

Update the GRUB configuration:

sudo grub-mkconfig -o /boot/grub/grub.cfg